/*
Copyright 2008 Ilya Novoselov

This file is part of TyuChat.

TyuChat is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

TyuChat is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with TyuChat.  If not, see <http://www.gnu.org/licenses/>.
*/

function submitInput(tab, prefix)
{
    url = "/submitInput";
    
    var inputSent = function(data) {
        // Empty input field
        $('input_field').value = "";
    }
  
    var error = function (err) {
        alert("Couldn't send input");
    };
    
    if(prefix) {
	    input = prefix + " " + $(tab + '_input').value;
    } else {
    	input = $(tab + '_input').value;
    }
    
    req = {
    	target: tab,
    	input: input,
    	ticket: $('ticket').value
    }
    
    var d = doSimpleXMLHttpRequest(url, req);
    d.addCallbacks(inputSent, error);
    
    return false;
};

// format timestamp
function formatTimestamp(timestamp)
{
    f = numberFormatter("00");
    
    h = timestamp.getHours();
    m = timestamp.getMinutes();
    s = timestamp.getSeconds();
    
    return f(h) + ":" + f(m) + ":" + f(s);
};

function addressNick(entry)
{
    nickname = entry.lastChild.textContent;
    input_field = $('input_field');
    input_field.value = nickname + ", " + input_field.value;
    input_field.focus();
};

// Stores last received event id.
lastEventId = null;

// userlist
users = new Array;

// current nickname

nickname = null;

function updateUserlist() {
    // users.sort(keyComparator("mode", "nickname"));
    users.sort(function(a, b) {
        rank_a = - "+%@&~".indexOf(a.mode);
        rank_b = - "+%@&~".indexOf(b.mode);
        return compare([rank_a, a.nickname.toLowerCase()],
            [rank_b, b.nickname.toLowerCase()])
    });
    
    ops = 0;
    total = 0;
    
    nodes = map(function(user) {        
        mode = TD(null, user.mode);
        nickname = TD(null, user.nickname);
        
        if(user.mode == '@') {
            ops += 1;
        } else {
            total += 1;
        };
        
        return TR({onclick: "addressNick(this)"}, mode, nickname);
    }, users);
    replaceChildNodes('userlist', nodes);
    
    $('userlist_stats').textContent = ops + " ops, " + total + " total"
};

function addUser(mode, nickname) {
    user = new Object;
    user.mode = mode;
    user.nickname = nickname;
    users.push(user);
    updateUserlist();
};

function deleteUser(nickname) {
    users = filter(function(user) {
        return (user.nickname != nickname);
    }, users);
    updateUserlist();
};

function getUser(nickname) {
    matching = filter(function(user) {
        return (user.nickname == nickname);
    }, users);
    
    return matching[0];
};

function changeUser(newname, oldname) {
    user = getUser(oldname);
    user.nickname = newname;
    updateUserlist();
};

activeTab = null;
openTabs = [];

// Create form for private conversation (no topic/userlist)
function buildPeerForm(peer) {
    form = DIV({id: peer + "_form"},       
        // Text frame.
        DIV({class: "peer_text frame"}, DIV({id: peer + '_messages', class: 'messages'})),
        
         // Input box.
        DIV({class: "input frame"}, INPUT({id: peer + "_input", type: "text"})),
        DIV({class: "buttons"},
            INPUT({type: "submit", value: "Say", onclick: "submitInput('" + peer + "')"}),
            INPUT({type: "submit", value: "Action", onclick: "submitInput('" + peer + "', '/me')"})));
     
    return form;
};

// Create new form for channel
function buildChannelForm(channel) {
	form = DIV({id: channel + "_form"},
	   // Userlist stats frame.
	    DIV({id: channel + "_stats", class: "userlist_stats"}),
        
        // Userlist frame
        DIV({class: "userlist_frame frame"},
            TABLE(null, TBODY({id: channel + "_userlist"}))),
        
        // Text frame.
        DIV({class: "text frame"}, DIV({id: channel + '_messages', class: 'messages'})),
        
        // Topic frame.
        DIV({class: "topic frame"}, INPUT({id: channel + "_topic", type: "text"})),

        // Input box.
        DIV({class: "input frame"}, INPUT({id: channel + "_input", type: "text"})),
        DIV({class: "buttons"},
            INPUT({type: "submit", value: "Say"}),
            INPUT({type: "submit", value: "Action"})));
     
    return form;
}

function addTab(tabLabel, form) {
    tabs = $('tabs');
    onclick = "switchTab('" + tabLabel + "')";
    tab = LI({id: tabLabel + "_tab", class: 'active', onclick: onclick}, tabLabel);
    appendChildNodes(tabs, tab);

    // Append form to document tree.
    appendChildNodes(document.body, form);

    switchTab(tabLabel);
    
    openTabs.push(tabLabel);
};

function addChannel(channel)
{
	form = buildChannelForm(channel);
	addTab(channel, form);
};

function addPeer(peer)
{
	form = buildPeerForm(peer);
	addTab(peer, form);
};

function switchTab(tabLabel)
{
	// Don't do anything is label already active.
	if(activeTab) {
	    if(activeTab != tabLabel) {
	        $(activeTab + '_tab').className = null;
	        hideElement(activeTab + '_form');
		    $(tabLabel + '_tab').className = 'active';
		    showElement(tabLabel + '_form');
		    activeTab = tabLabel;
	    };
	};
	   
    activeTab = tabLabel;
};

function getUserChannels(user)
{
	// FIXME: stub
	return openTabs;
};

function printMessage(tabs, msg)
{
	forEach(tabs, function (tab) {
	    messagesDiv = $(tab + '_messages');
	    if(!messagesDiv)
	        messagesDiv = addChannel(tab);
	    appendChildNodes(messagesDiv, msg, BR());
	});
}

function errorMessage(msg)
{
    printMessage([activeTab], "Error: " + msg);
};

function handleEvent(event) {
    message = null;
    timestamp = isoTimestamp(event.timestamp);
    
    isPeerMessage = false;
    
    if(event.channel == $('nickname').value) {
    	peer = event.user.split("!", 1);
        isPeerMessage = true;
    };

    switch(event.type) {
        case 'names':
            forEach(event.names, function(name) {
                addUser(name.mode, name.nickname);
            });
            updateUserlist();
            break;
        case 'privmsg':
            nickname = event.user.split("!", 1);
            message = "<" + nickname + "> " + event.msg;
            if(isPeerMessage) {
            	// Open peer tab, if not exists.
            	openPeerTab(nickname);
            	tabs = [nickname];
            } else {
            	tabs = [event.channel];
            }
            break;
        case 'action':
            nickname = event.user.split("!", 1);
            message = "* " + nickname + " " + event.data;
            if(isPeerMessage) {
            	// Open peer tab, if not exists.
                openPeerTab(nickname);
                tabs = [nickname];
            } else {
                tabs = [event.channel];
            }
            break;
        case 'notice':
            nickname = event.user.split("!", 1);
            message = "-" + nickname + "-" + event.msg;
            tabs = [activeTab];
            break;
        case 'join':
            addUser(event.channel, event.user);
            message =  "--> " + event.user + " has joined " + event.channel;
            tabs = [event.channel];
            break;
        case 'kick':
            deleteUser(event.channel, event.kickee);            
            message = "<-- " + event.kicker + " was kicked by " + event.kickee
                + " (" + event.message + ")";
            tabs = [event.channel];
            break;
        case 'leave':
            deleteUser(event.channel, event.user);
            message = "<-- " + event.user + " left " + event.channel;
            tabs = [event.channel];
            break;
        case 'quit':
            nickname = event.user;
            
            forEach(userChannels[nickname], function (channel) {
                deleteUser(channel, nickname);
            });

            message = "<-- " + event.user + " has quit ("
                + event.quitMessage + ")";
            
            tabs = userChannels[nickname].concat([nickname]);

            break;
        case 'nick':
            changeUser(event.newname, event.oldname);
            message = "*** " + event.oldname + " is now know as "
                + event.newname;
            tab = userChannels[nickname].concat([nickname]);
            forEach(getUserTabs(nickname), function (tab)) {
                
            }
            break;
        case 'signedOn':
            message = "*** Connection established"
            tabs = [activeTab];
            break;
        case 'joined':
            message = "*** Now talking on " + event.channel;
            tabs = [event.channel];
            break;
        case 'nickChanged':
            nickname = event.nick;
            $('nickname').value = nickname;
            message = "*** You are now known as " + nickname;
            tabs = openTabs;
            break;
        case 'connectionLost':
            message = "*** Connection lost (" + event.reason + ")";
            chanels = openTabs;
            break;
        case 'disconnect':
            message = "*** Disconnected";
            tabs = openTabs;
            break;
        case 'error':
            message = "***  " + event.message;
            tabs = [activeTab];
            break;
        default:
            errorMessage("*** Unknown event type (" + event.type + ")")
    };
    
    if(message) {
	    message = "[" + formatTimestamp(timestamp) + "] " + message;
	    printMessage(tabs, message);
    };
};

// fetch new events and put them into window
function fetchEvents()
{
    url = "/getEvents";
   
    var gotEvents = function (events) {
        // Display events.
        forEach(events, function(event) {
            lastEventId = event.id;
            
            textFrame = $(activeTab + '_messages');
            needScroll = false;
            
            // Scroll only if window is at the bottom
            if(textFrame.scrollHeight - textFrame.scrollTop == textFrame.clientHeight)
                needScroll = true;
            
            handleEvent(event);   
        });
        
        // Scroll messages frame.
        if(needScroll)
            textFrame.scrollTop = textFrame.scrollHeight;
        
        // Start request again.
        fetchEvents();
    };
    
    var error = function (err) {
        if(err instanceof XMLHttpRequestError & err.number == 404) {
            errorMessage("FATAL: Ticket not found");
        } else {
            errorDelay = 15;
            errorMessage("Error trying to fetch events (" + repr(err) + "), retry in " + errorDelay + " seconds");
            callLater(errorDelay, fetchEvents);
        }
    };
    
    // Get ticket.
    ticket = $('ticket').value;
    
    var d = loadJSONDoc(url, {lastId: lastEventId, ticket: ticket});
    d.addCallbacks(gotEvents, error);
};


function start()
{
	server = $('server').value;
	
	// Server tab.
    addPeer(server);
    
    // start fetchEvents sequence
    fetchEvents();
}

addLoadEvent(start);